package com.qust.enDecrpytion.proxy.encryption;

import com.qust.enDecrpytion.annotations.EnableEnDecryption;
import com.qust.enDecrpytion.annotations.Encryption;
import com.qust.enDecrpytion.proxy.AbstractEnDecryptionProxy;
import com.qust.enDecrpytion.proxy.ChainCall;
import com.qust.enDecrpytion.proxy.parser.DefaultParser;
import com.qust.enDecrpytion.proxy.parser.Parser;
import com.qust.enDecrpytion.proxy.parser.ParserPOJO;
import com.qust.enDecrpytion.proxy.status.TypeHandleContext;
import com.qust.encryptionAndDecryption.IEnDecryption;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 加密代理可以分为 1 种: 参数加密 : 对参数进行加密操作
 * 参数类型可以为:
 * 1.字符串    以及其他基本类型
 * 2.pojo类   对标有@Encryption的属性进行加密
 * 3.list     list里面如果为基本类型全部加密,如果为pojo类加密标有@Encryption的属性
 * 4.数组      转为list进行加密
 * 5.map      key不变,value加密
 * 注意:以后或许出现新情况,该抽象类用于拓展
 */
public class EncryptionProxy extends AbstractEnDecryptionProxy implements ChainCall {
    private static final Logger logger = LogManager.getLogger(EncryptionProxy.class);
    private TypeHandleContext context;

    private Map<String, ParserPOJO> parserPOJOMap = new HashMap<>();
    private Parser parser = new DefaultParser();


    public EncryptionProxy(Object target, IEnDecryption enDecryption, TypeHandleContext context) {
        this.targetClass = target.getClass();
        this.targetObject = target;
        this.enDecryption = enDecryption;
        this.context = context;
    }


    @Override
    public Object call() {
        logger.info("创建加密层");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(new EncryptionProxy(this.targetObject, this.enDecryption, this.context));
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//        Parameter[] parameters = method.getParameters();
//        parametersEnDecryption(parameters, objects);
//        logger.info("{}方法执行", method.getName());
//        return returnEnDecryption(method, method.invoke(targetObject, objects));
        if (method.isAnnotationPresent(EnableEnDecryption.class)){

            String name = method.getName(); // 获取方法名作为存储解析元数据的key
            if (!parserPOJOMap.containsKey(name)){ // 解析注解
                ParserPOJO parser1 = parser.parser(method, Encryption.class);
                parserPOJOMap.put(name,parser1);
            }

            ParserPOJO parserPOJO = parserPOJOMap.get(name);
            ArrayList<Integer> parameters = parserPOJO.getParameters(); // 参数
            parameters.forEach(integer -> objects[integer] = context.handle(objects[integer],false,Encryption.class, s -> enDecryption.encryption(s)));

            Object result = method.invoke(targetObject, objects); // 执行方法

            return parserPOJO.isReturnType()?context.handle(result, false,Encryption.class, s -> enDecryption.encryption(s)):result; // 返回值
        }
        return method.invoke(targetObject, objects);
    }

    public Object[] parametersEnDecryption(Parameter[] parameters, Object[] args) {
        for (int i = 0; i < parameters.length; i++) {
            if (parameters[i].isAnnotationPresent(Encryption.class)) {
                logger.debug("{}参数加密", parameters[i].getName());
                args[i] = context.handle(args[i], false,Encryption.class, s -> enDecryption.encryption(s));
            }
        }
        return args;
    }

    public Object returnEnDecryption(Method method, Object result) {
        if (method.isAnnotationPresent(Encryption.class)) {
            logger.debug("{}方法返回值加密", method.getName());
            return context.handle(result, false,Encryption.class, s -> enDecryption.encryption(s));
        }
        return result;
    }
}
